﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using Kugar.Core.ExtMethod;

namespace Kugar.Core.Communications.MasterSlaveCommunication.SerialPortCommunication.StandardModbus
{
    public class SerialPortModbusMasterController : MasterSideBase
    {
        private readonly SerialPort sp = null;
        private SendQueue<DataStructToSendBase> sq = new SendQueue<DataStructToSendBase>();

        private Timer checkSendEmpty = null;
        private Timer checkRevOutTimer = null;

        private bool isReceived = false;

        public SerialPortModbusMasterController(SerialPortConnectConfig config)
        {
            sp = new SerialPort(config.portName, config.baudRate, config.parity, config.dataBits, config.stopBits);

            try
            {
                sp.Open();
            }
            catch
            {

            }

            sp.DataReceived += SerialPort_DataReceived;
            checkRevOutTimer = new Timer(checkRevOut, checkRevOutTimer, 1000,0 );
            checkSendEmpty = new Timer(sendData, checkSendEmpty,SendDelayTime,0);
        }

        public override void SendAsyn(DataStructToSendBase sendStruct)
        {
            sq.EnQueue(sendStruct);
        }


        public override DataStructToSendBase[] RemoveSendingStruct(Predicate<DataStructToSendBase> checker)
        {
            return sq.GetOutHostCmd(checker);
        }

        protected override bool CheckReceivedData(DataStructToSendBase sendedStruct, byte[] data)
        {
            throw new NotImplementedException();
        }

        protected override bool CheckReceivedData(byte[] data)
        {
            if (data != null && data.GetCRCCode(0, data.Length) == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
            //return data != null && data.GetCRCCode(0, data.Length) == 0;
        }

        protected override void Dispose(bool disposing)
        {
            CloseTimer();

            lock (sq)
            {
                sq.Close();
            }

            
            checkSendEmpty.Dispose();
            checkRevOutTimer.Dispose();

            GC.SuppressFinalize(this);
        }

        private void sendData(object state)
        {
            if (dataSended == null)
            {
                var data = sq.DeQueue();
                Exception except = null;
                //try
                //{

                //    base.OnBeforceSendData(data,null);
                //    data.OnBeforceSendData(this);
                //}
                //catch
                //{

                //}

                base.OnBeforceSendData(data,null);

                try
                {
                    sp.Write(data.Data, 0, data.Data.Length);
                }
                catch(Exception ex)
                {
                    except = ex;
                    ///TODO:引发发送异常
                }
                finally
                {
                    Interlocked.Exchange(ref dataSended, data);
                    isReceived = false;
                }

                base.OnAfterSendData(data,except);

                //try
                //{
                //    this.OnAfterSendData(dataSended, except);
                //    data.OnAfterSendData(this,except);
                //}
                //catch (Exception)
                //{
                    
                //}

            }

            checkSendEmpty.Change(SendDelayTime, 0);
        }

        private void checkRevOut(object state)
        {
            if (dataSended != null && isReceived == false)
            {
                lock (dataSended)
                {
                    if (dataSended != null && isReceived==false) //再次检查是否为空，可以防止在if之后，锁定之前dataSended变量的值被改变
                    {
                        if (dataSended.SendTime > dataSended.SendTime.AddMilliseconds(dataSended.SendTimeOutTime))
                        {
                            if (dataSended.ReSendCount > 0)
                            {
                                sq.EnQueue(dataSended);
                            }
                            else
                            {
                                DropItem(dataSended);
                                this.OnAfterReceiveData(null, new ReceiveTimeOut());
                            }
                            
                            Interlocked.Exchange(ref dataSended, null);
                        }
                    }
                }
            }

            checkRevOutTimer.Change(1000, Timeout.Infinite);
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (dataSended == null || dataSended.Data.Length <= 0 || !sp.IsOpen)
            {
                return;
            }

            isReceived = true;

            checkSendEmpty.Change(Timeout.Infinite, Timeout.Infinite);

            DataStructToSendBase tempSend = null;

            Interlocked.Exchange(ref tempSend, dataSended);

            //CloseTimer();

            byte[] buf = null;

            lock (sp)
                {
                    try
                    {
                        //判断串口数据是否接收完成的标准为连续50ms没有再接收到新数据(即50ms内,缓冲区数据已读取字节数不变)
                        var ti = sp.BytesToRead; //用于判断是否已经接收完成全部数据
                        var o = 0;
                        while (o <= 5)
                        {
                            Thread.Sleep(10);
                            var t1 = sp.BytesToRead;

                            if (t1 == ti)
                                o++;
                            else
                                o = 0;

                            ti = t1;
                        }

                        if (sp.BytesToRead <= 3) return;

                        buf = new byte[sp.BytesToRead];
                        sp.Read(buf, 0, buf.Length);

                    }
                    catch (Exception)
                    {
                        //OpenTimer();
                        //sp.Close();
                        Interlocked.Exchange(ref dataSended, null);
                        checkSendEmpty.Change(this.SendDelayTime, 0);
                        return;
                    }
                }

            Exception ex = null;

            if (CheckReceivedData(buf))
            {
                //OpenTimer();

                tempSend.ReSendCount--;

                if (tempSend.ReSendCount>0)
                {
                    SendAsyn(tempSend);
                }
                else
                {
                    DropItem(tempSend);
                }

                ex = new ReceiveDataCheckError();
                
            }
            //else
            //{
            //     this.OnAfterReceiveData(buf, null);
            //}

            this.OnAfterReceiveData(buf,ex );

            //Thread.Sleep(this.SendDelayTime);

            Interlocked.Exchange(ref dataSended, null);

            checkSendEmpty.Change(this.SendDelayTime, 0);

            //OpenTimer();

        }

        private void DropItem(DataStructToSendBase item)
        {
            item.Dispose();
        }

        private void CloseTimer()
        {
            checkRevOutTimer.Change(Timeout.Infinite, Timeout.Infinite);
            checkSendEmpty.Change(Timeout.Infinite, Timeout.Infinite);
        }

        private void OpenTimer()
        {
            checkRevOutTimer.Change(1000,0);
            checkSendEmpty.Change(SendDelayTime,0);
        }

        ~SerialPortModbusMasterController()
        {
            base.Dispose();
        }
    }


}
